<template>
  <div
    class="cl-input"
    :class="{
      [`cl-input--${type}`]: type,
      [`cl-input--${size}`]: size,
      'is-disable': disabled,
      'is-prepend': $slots.prepend,
      'is-append': $slots.append,
      'is-prefix': $slots.prefix,
      'is-suffix': $slots.suffix,
      'is-focus': isFocus
    }">
    <!-- input -->
    <template v-if="type !== 'textarea'">
      <!-- prepend slot -->
      <div v-if="$slots.prepend" class="cl-input__prepend">
        <slot name="prepend"></slot>
      </div>
      <div class="cl-input__wrapper">
        <!-- prefix slot -->
        <span v-if="$slots.prefix" class="cl-input__prefix">
          <slot name="prefix"></slot>
        </span>
        <input
          ref="inputRef"
          v-bind="attrs"
          :type="showPassword ? (passwordVisible ? 'text' : 'password') : type"
          class="cl-input__inner"
          :disabled="disabled"
          v-model="innerValue"
          :readonly="readonly"
          :placeholder="placeholder"
          :autocomplete="autocomplete"
          :autofocus="autofocus"
          @change="handleChange"
          @input="handleInput"
          @focus="handleFocus"
          @blur="handleBlur" />
        <!-- suffix slot -->
        <span
          v-if="$slots.suffix || showClear || showPasswordArea"
          class="cl-input__suffix"
          @click="keepFocus">
          <slot name="suffix"></slot>
          <Icon
            icon="circle-xmark"
            v-if="showClear"
            class="cl-input__clear"
            @click="clear"
            @mousedown.prevent="NOOP"></Icon>
          <Icon
            :icon="passwordVisible ? 'eye' : 'eye-slash'"
            v-if="showPasswordArea"
            class="cl-input__password"
            @click="togglePassowrdVisible"></Icon>
        </span>
      </div>
      <!-- append slot -->
      <div v-if="$slots.append" class="cl-input__append">
        <slot name="append"></slot>
      </div>
    </template>
    <!-- textarea-->
    <template v-else>
      <textarea
        ref="inputRef"
        v-bind="attrs"
        class="cl-textarea__wrapper"
        :disable="disabled"
        v-model="innerValue"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur" />
    </template>
  </div>
</template>

<script lang="ts" setup>
import { ref, watch, computed, useAttrs, nextTick, inject } from 'vue'
import type { InputProps, InputEmits } from './types'
import Icon from '../Icon/Icon.vue'
import { formItemContextKey } from '../Form/types'
defineOptions({
  name: 'CLInput',
  inheritAttrs: false
})
const props = withDefaults(defineProps<InputProps>(), {
  type: 'text',
  autocomplete: 'off'
})
const emits = defineEmits<InputEmits>()
const attrs = useAttrs()
const innerValue = ref(props.modelValue)
const isFocus = ref(false)
const passwordVisible = ref(false)
const inputRef = ref<HTMLElement | null>(null)
const formItemContext = inject(formItemContextKey)
const runValidation = (trigger?: string) => {
  formItemContext?.validate(trigger).catch(e => {
    console.log(e)
  })
}
const showClear = computed(() => {
  return (
    props.clearable && !props.disabled && !!innerValue.value && isFocus.value
  )
})
const showPasswordArea = computed(() => {
  return props.showPassword && !props.disabled && !!innerValue.value
})
const togglePassowrdVisible = () => {
  passwordVisible.value = !passwordVisible.value
}
const handleChange = () => {
  emits('change', innerValue.value)
  runValidation('change')
}
const handleFocus = (event: FocusEvent) => {
  isFocus.value = true
  emits('focus', event)
}
const handleBlur = (event: FocusEvent) => {
  isFocus.value = false
  emits('blur', event)
  runValidation('blur')
}
const keepFocus = async () => {
  await nextTick()
  inputRef.value?.focus()
}
const clear = () => {
  innerValue.value = ''
  emits('update:modelValue', '')
  emits('clear')
  emits('input', '')
  emits('change', '')
}
const handleInput = () => {
  emits('update:modelValue', innerValue.value)
  emits('input', innerValue.value)
  runValidation('input')
}
const NOOP = () => {}
watch(
  () => props.modelValue,
  newValue => {
    innerValue.value = newValue
  }
)
defineExpose({
  ref: inputRef
})
</script>
